home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Sound Cards
/
Programming Sound Cards.iso
/
sound_45
/
mod_dis.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-01-01
|
33KB
|
1,127 lines
/* BEGIN DAVID LAI COPYRIGHT ********************************************* */
/*
(C) Copyright David Lai 1993, 1994. All rights reserved.
The source code is copyright by David Lai, however it is freely
distributable and released for unrestricted use. Users may copy or modify
this source code without charge, provided all copyright
notices remain intact in all the source code. Portions of the source code
copyright by their respective copyright holders and are covered
under different agreements, however the source code used has
specifically been marked distributable royalty-free.
You can do whatever you want with it, even charge money for it, if
you find a sucker willing to pay for it. This is not shareware, the program
is not crippled in any way, do not send money. You can e-mail comments
to the electronic mail address below, or fax to the fax number below.
If you add a feature thats useful, or do a new port, you can send
the context diffs to the e-mail address below. I may include it in
subsequent releases. Your code must specifically be marked
freely distributable, I will not include code marked otherwise.
THE SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
The source code is provided with no support and without any obligation on
the part of David Lai to assist in its use, correction,
modification or enhancement.
DAVID LAI SHALL HAVE NO LIABILITY WITH RESPECT TO THE
INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
OR ANY PART THEREOF.
In no event will David Lai be liable for any lost revenue
or profits or other special, indirect and consequential damages, even if
David Lai has been advised of the possibility of such damages.
David Lai
1370 McKendrie St
San Jose, CA 95126
fax: 408-241-4615
lai%fastfood@daver.bungi.com
*/
/* END DAVID LAI COPYRIGHT ********************************************* */
/* dis-assemble a mod file */
#include "misc.h"
#include "modcomp.h"
#include "wav.h"
#include "mod_disv.c" /* some initialized arrays */
/* ALLOW_CONTROLS will allow control characters and non-printing characters
in the name of the mod */
#undef ALLOW_CONTROLS
/* RPT_LENGTH_IS_INCR interprets the rpt_length field of samples as 1 more
than the actual number of words to repeat, for example:
length = 500
rpt_offset = 0
rpt_length = 500
The user really meant to say:
length = 500 (998 bytes of real sample)
rpt_offset = 0 (begin the repeat at the first byte of the real sample)
rpt_length = 499 (repeat entire 998 bytes of the sample)
Without RPT_LENGTH_IS_INCR, the above will generate warning message that
there is an attempt to repeat beyond the end of the sample (1000 bytes)
*/
#define RPT_LENGTH_IS_INCR
static struct sample samples[MAX_INSTRUMENTS];
static int channels=0, instruments=0, num_patterns;
static char mod_tag[MOD_TAGSIZE+1]="";
static char mod_name[MOD_NAME_LENGTH+1];
static int verbose=0;
static int adj, tagsize=MOD_TAGSIZE;
static unsigned char num_patterns_in_song, end_jump_pos;
static unsigned char pattern_table[PATTERN_TABLE_SIZE];
static uint32 file_size;
static int pattern_size;
static off_t fixed_size;
static char *mod_format="mod";
static int file_has_gaps=0;
static int dump_unused_patterns=0;
static uint32 wav_sample_rate=16574; /* .wav sample rate */
/* initially set to the middle C rate */
static int wav_info_flag = 1; /* dump INFO unless requested not to */
#define PATTERN_SPACE ((uint32)num_patterns * pattern_size)
#ifdef __STDC__
read_tag0(FILE *fi, off_t offset, char tag[MOD_TAGSIZE+1])
#else
read_tag0(fi, offset, tag) FILE *fi; off_t offset; char tag[MOD_TAGSIZE+1];
#endif
{
/* read the 4 byte tag at the offset specified and attempt to interpret */
if (fseek(fi, offset, SEEK_SET)!=0)
err_exit("Badly formed MOD file: cant seek to tag", NULL);
if (fread(tag, sizeof(char), MOD_TAGSIZE, fi) != MOD_TAGSIZE)
err_exit("Badly formed MOD file: cant read tag",NULL);
tag[MOD_TAGSIZE]='\0';
if (strcmp(tag,"M.K.")==0 || strcmp(tag,"FLT4")==0 || strcmp(tag,"M!K!")==0 ||
strcmp(tag,"4CHN")==0 || strcmp(tag,"M&K!")==0 )
{
channels = 4;
return 1; /* success */
}
else if (strcmp(tag,"6CHN")==0)
{
channels = 6;
return 1; /* success */
}
else if (strcmp(tag,"8CHN")==0 || strcmp(tag,"OCTA")==0)
{
channels = 8;
return 1; /* success */
}
else
return 0; /* dont know this tag */
}
#ifdef __STDC__
read_tag(FILE *fi)
#else
read_tag(fi) FILE *fi;
#endif
{
/* read and interpret tag to determine file format */
char tag[MOD_TAGSIZE+1], tag15[MOD_TAGSIZE+1];
/* check if it is a 31 instr file first */
if (read_tag0(fi, TAG_OFFSET_31, tag)==1)
{
instruments = 31;
strcpy(mod_tag, tag);
return 1;
}
/* try again with 15 instr */
if (read_tag0(fi, TAG_OFFSET_15, tag15)==1)
{
instruments = 15;
strcpy(mod_tag, tag15);
return 1;
}
/* take a guess that it is 4 track */
if (verbose) fprintf(stderr, "Warning: Unknown tag 31=%s 15=%s\n", tag, tag15);
fprintf(stderr,"Unknown mod format - assuming -c 4 -i 15\n");
channels = 4;
instruments = 15;
tagsize = 0;
#if 0
{
int c;
/* check if there is printable chars at location 470 */
if (fseek(fi, CHECK_31_INST_OFFSET, SEEK_SET)!=0)
err_exit("Unable to seek to check", NULL);
c=getc(fi);
if (isascii(c) && isprint(c)) /* this test may not work all the time */
{
instruments = 31;
strcpy(mod_tag, tag15);
}
else
{
instruments= 15;
strcpy(mod_tag, tag15);
}
}
#endif
return 1;
}
void
#ifdef __STDC__
verify_format(FILE *fi)
#else
verify_format(fi) FILE *fi;
#endif
{
/* verify the name of the mod is valid */
int i,j;
uint32 total_sample_size;
if (fseek(fi, 0L, SEEK_SET)!=0)
err_exit("Badly formed MOD file", NULL);
if(fread(mod_name, sizeof(char), MOD_NAME_LENGTH, fi) != MOD_NAME_LENGTH)
err_exit("Badly formed MOD file - mod name", NULL);
mod_name[MOD_NAME_LENGTH]='\0';
/* check all chars are valid */
for (i=0; i< MOD_NAME_LENGTH; i++)
if ((!isascii(mod_name[i])) || (!isprint(mod_name[i]) && mod_name[i]!='\0'))
#ifndef ALLOW_CONTROLS
err_exit("Bad mod name, character 0x%x",(unsigned char)mod_name[i]);
#else
fprintf(stderr, "Bad mod name, character 0x%x\n",(unsigned char)mod_name[i]);
#endif
/* verify that all sample headers are OK */
for (j=0; j< instruments; j++)
{
if (fseek(fi, SAMPLE_HEADER_OFFSET +
SAMPLE_HEADER_LENGTH *j, SEEK_SET)!=0)
err_exit("Badly formed MOD file, cant read sample headers", NULL);
if (fread( samples[j].name, sizeof(char), SAMPLE_NAME_SIZE, fi) !=
SAMPLE_NAME_SIZE)
err_exit("Cant read sample name %d",j);
samples[j].name[SAMPLE_NAME_SIZE]='\0';
if (verbose > 1)
{
for(i=0; samples[j].name[i]!='\0' && i< SAMPLE_NAME_SIZE; i++)
if (!isprint(samples[j].name[i]) && isascii(samples[j].name[i]))
fprintf(stderr,"Warning: Bad character in sample #%d name, character 0x%x in %s\n",
j, (unsigned char)samples[j].name[i], samples[j].name);
}
samples[j].length = rbshort(fi);
samples[j].orig_length = samples[j].length;
if (samples[j].length == (SAMPLE_IGNORE_BYTES / 2))
{
if (verbose > 1)
fprintf(stderr,"Warning: zeroing sample %d length (was %u)\n",
j,samples[j].length);
samples[j].length = 0;
adj+= (SAMPLE_IGNORE_BYTES / 2);
/* adjustment for filelength checks */
}
if (fread( &samples[j].finetune, 1, 1, fi)!=1)
err_exit("Cant read sample %d's finetune", j);
/* sign extend finetune */
samples[j].finetune = SIGN_EXT_NIBBLE(samples[j].finetune);
if (fread( &samples[j].volume, 1, 1, fi)!=1)
err_exit("Cant read sample %d's volume", j);
if (samples[j].volume > 64)
{
if (verbose)
fprintf(stderr,"Warning: volume for sample %d out of range %d, set to 64\n",j, samples[j].volume);
samples[j].volume = 64;
}
samples[j].rpt_offset = rbshort(fi);
samples[j].rpt_length = rbshort(fi);
if (samples[j].length)
{
#ifdef RPT_LENGTH_IS_INCR
/* we assume rpt_length is actually 1 more than the actual # of
words to repeat, probably represents the AMIGA word */
if (verbose>1 && samples[j].rpt_length==0)
fprintf(stderr,"rpt_length==0 in sample %d\n",j);
if (samples[j].rpt_length && samples[j].rpt_length==(samples[j].length-samples[j].rpt_offset-1))
{
if (verbose)
fprintf(stderr,"LAI length+offset+1=length in sample %d\n",j);
}
else if (samples[j].rpt_length) samples[j].rpt_length--;
#else
/* rpt_length of 1 is interpreted as 0 - this has the unfortunate
side-effect
that you cant repeat a loop of 2 bytes (probably wont sound too
good anyways). There are just too many mods that set
rpt_length to 1 when they really mean 0. */
if (samples[j].rpt_length==1)
{
if (verbose > 1)
fprintf(stderr,"Warning: rpt_length for sample %d zeroed\n",
j);
samples[j].rpt_length=0;
}
#endif
/* we use >= below because we assume the rpt_offset is an offset into
the real data (without the dummy amiga word) */
if (samples[j].rpt_offset >= samples[j].length)
{
if (verbose)
fprintf(stderr,"Warning: rpt_offset for sample %d out of range %lu (words), looping turned off\n",j, (unsigned long)samples[j].rpt_offset);
samples[j].rpt_offset = samples[j].length;
samples[j].rpt_length = 0;
}
if (samples[j].rpt_length !=0 &&
(unsigned long) samples[j].rpt_offset+samples[j].rpt_length >= samples[j].length)
{
#ifdef RPT_LENGTH_IS_INCR
if (verbose)
#else
if (verbose>1 ||
(((unsigned long)samples[j].length-samples[j].rpt_offset != (unsigned long)samples[j].rpt_length )
&& verbose))
#endif
fprintf(stderr,"Warning: rpt_length for sample %d out of range %lu, set to %lu (words)\n",
j, (unsigned long)samples[j].rpt_length,
(unsigned long)samples[j].length-samples[j].rpt_offset-1);
samples[j].rpt_length = samples[j].length-samples[j].rpt_offset-1;
}
} /* if length!=0 */
}
/* verify that patterns header is OK */
if (fseek(fi, SAMPLE_HEADER_OFFSET + SAMPLE_HEADER_LENGTH * instruments,
SEEK_SET)!=0)
err_exit("Badly formed MOD file, cant reach pattern info",NULL);
if (fread( &num_patterns_in_song, 1, 1, fi)!=1)
err_exit("Cant read num_patterns_in_song",NULL);
if (num_patterns_in_song==0 || num_patterns_in_song > 128)
err_exit("Badly formed MOD file, num_patterns_in_song==%d",num_patterns_in_song);
if (fread( &end_jump_pos, 1, 1, fi)!=1)
err_exit("Cant read end_jump_pos",NULL);
if (end_jump_pos <= MAX_VALID_END_JUMP_POS && end_jump_pos >= num_patterns_in_song)
{
/* end_jump_pos==120 is used by some trackers to indicate no looping,
this should really be set to 127 or some other value>128. Too
bad for compatibility the end_jump_pos is in the valid range
for pattern positions (1-128) when it indicates no looping. Since
the ==120 case is so common, we will only issue a diagnostic when
verbose is set high.
*/
if (verbose>1 || ( verbose && end_jump_pos!=120))
fprintf(stderr, "Warning: end_jump_pos out of range %d, looping disabled\n", end_jump_pos);
end_jump_pos = MAX_VALID_END_JUMP_POS+1;
}
/* verify that all patterns are OK */
if (fread( pattern_table, sizeof(pattern_table[0]), PATTERN_TABLE_SIZE, fi) !=
PATTERN_TABLE_SIZE)
err_exit("Cant read pattern table",NULL);
/* calculate the total # of patterns stored in the file, we do this by
subtracting from the file size the fixed portion of the file, and all
the sample sizes, and dividing by the pattern size */
pattern_size = NOTESIZE * channels * LINES_PER_PATTERN;
fixed_size=SAMPLE_HEADER_OFFSET + SAMPLE_HEADER_LENGTH * instruments + 2 +
sizeof(pattern_table) + tagsize;
for (j=0, total_sample_size=0; j< instruments; j++)
{
total_sample_size += (unsigned long)samples[j].length * 2;
}
if (file_size-fixed_size < total_sample_size)
err_exit("Badly formed MOD file: file too short",NULL);
num_patterns=(int)((file_size-fixed_size-total_sample_size) / pattern_size);
if (num_patterns <1 || num_patterns > 256)
err_exit("Badly formed MOD file: num_patterns==%d",num_patterns);
if ((PATTERN_SPACE + fixed_size+total_sample_size != file_size)
&& (PATTERN_SPACE + fixed_size+total_sample_size + adj*2 != file_size))
{
if (verbose)
fprintf(stderr,"Warning: excess data in file, %ld bytes\n",
file_size - (PATTERN_SPACE + fixed_size+
total_sample_size));
/* if the excess data is within 4 bytes of the pattern size,
truncate the last sample and assume there is 1 more
pattern */
if ((int)(file_size - (PATTERN_SPACE + fixed_size+total_sample_size)) >=
pattern_size - 4 )
{
int trunc, last_inst;
trunc = pattern_size - (int)(file_size - (PATTERN_SPACE + fixed_size+total_sample_size));
if (trunc%2 ==0)
{ /* must be even number */
/* find last "real" instrument */
for (j=0,last_inst=0; j< instruments; j++)
{
if (samples[j].length!=0) last_inst=j;
}
samples[last_inst].length -= trunc/2;
samples[last_inst].orig_length -= trunc/2;
/* need to truncate rpt_length only if it loops to end of
sample */
if (samples[last_inst].rpt_length + samples[last_inst].rpt_offset >= samples[last_inst].length)
{
int max_rpt = samples[last_inst].length - samples[last_inst].rpt_offset - 1;
if (max_rpt < 1)
{
samples[last_inst].rpt_length = 0; /* too short */
samples[last_inst].rpt_offset = 0;
}
else
{
samples[last_inst].rpt_length = max_rpt;
}
}
num_patterns++;
total_sample_size -= trunc;
if (verbose)
fprintf(stderr,"Warning: truncating last sample (#%d) by %d bytes to fix size\n", last_inst, trunc);
}
}
}
/* we will assume that if the file size matches the adjusted size, then there
are real gaps in the samples. Probably caused by poorly written
software which inserts a couple of bytes for empty samples */
if (adj!=0 && PATTERN_SPACE +fixed_size+total_sample_size+ adj*2 == file_size)
file_has_gaps = 1;
else
file_has_gaps = 0;
/* verify all pattern indexes in pattern_table are valid */
for(i=0; i<PATTERN_TABLE_SIZE; i++)
{
if (pattern_table[i]>=num_patterns)
err_exit("Illegal pattern number %d",pattern_table[i]);
}
}
void
#ifdef __STDC__
assign_filenames(char *prefix, char *suffix)
#else
assign_filenames(prefix, suffix) char *prefix; char *suffix;
#endif
{
int j;
char namebuf[256];
for (j=0; j< instruments; j++)
{
if (samples[j].length != 0)
{
sprintf(namebuf, "%s%d%s", prefix?prefix:"inst", j,
suffix?suffix:"");
if ((samples[j].sample_filename=malloc(strlen(namebuf)+1))==NULL)
{
err_exit("Out of Memory", NULL);
}
strcpy(samples[j].sample_filename, namebuf);
}
}
}
#ifdef __STDC__
void write_headers(char *path)
#else
void write_headers(path) char *path;
#endif
{
if (verbose) printf("// Name of File\n");
printf("mod_filename %s\n",path);
if (verbose) printf("// Name of Song\n");
printf("mod_name %s\n",mod_name);
if (tagsize)
{
if (verbose) printf("// Tag\n");
printf("mod_tag %s\n",mod_tag);
}
if (verbose) printf("// Format\n");
printf("format %s\n",mod_format);
if (verbose) printf("// Number of instruments (samples), and channels (voices, tracks)\n");
printf("instruments %d channels %d\n", instruments, channels);
}
void
#ifdef __STDC__
write_sample_headers(enum sample_format sample_format)
#else
write_sample_headers(sample_format) enum sample_format sample_format;
#endif
{
int j;
for (j=0; j< instruments; j++)
{
if (samples[j].length != 0 || strlen(samples[j].name)!=0)
{
if (verbose) printf("\n// Sample # %d, bytes: %lu\n", j,
(samples[j].length!=0)?
(unsigned long) samples[j].length * 2 - SAMPLE_IGNORE_BYTES:
#ifdef NO_CONSTANT_SUFFIX
0);
#else
0UL);
#endif
printf("assume sample_number %d\n", j);
printf(" sample_name %s\n", samples[j].name);
if( samples[j].length != 0)
{
if (samples[j].finetune != 0) printf(" sample_finetune %d\n",samples[j].finetune);
if (samples[j].volume!= 64) printf(" sample_volume %d\n", samples[j].volume);
if (samples[j].rpt_length != 0)
{
printf(" repeat_offset %lu repeat_length %lu\n",
(unsigned long)samples[j].rpt_offset* 2,
(unsigned long)samples[j].rpt_length*2);
}
if (sample_format_table[sample_format].name)
printf(" sample_format %s\n", sample_format_table[sample_format].name);
if (samples[j].sample_filename)
printf(" sample_filename %s\n", samples[j].sample_filename);
}
}
}
}
void
#ifdef __STDC__
write_pattern_header(void)
#else
write_pattern_header()
#endif
{
int i, j;
if (verbose) printf("\n// Pattern sequence for song\n");
printf("pattern = {");
for (i=0; i< num_patterns_in_song; i+=10)
{
printf("\t");
for ( j = i; j < num_patterns_in_song && j < i+10; j++)
printf("%d%s%s", pattern_table[j], (j==num_patterns_in_song-1)?"":",",
(j==num_patterns_in_song-1 || j==i+9)?"":" ");
if (num_patterns_in_song > 10) printf("\n");
}
printf(" }\n");
if (end_jump_pos <= MAX_VALID_END_JUMP_POS) printf("end_jump_pos %d\n",end_jump_pos);
}
#ifdef __STDC__
int note_empty(union u_note *p)
#else
int note_empty(p) union u_note *p;
#endif
{
/* test if the note is empty */
/* if a sample is specified, or a period is specified, it is a real note */
if (SAMPLE_NUMBER(p) != 0 || SAMPLE_PERIOD(p)!=0) return 0;
/* if there is a valid command, then it is a trigger as well */
/* otherwise an invalid command is ignored */
if (EFFECT(p) == Arpeggio && ARG1(p)==0 && ARG2(p)==0) return 1;
/* invalid Arpeggio command */
/* everything else is a valid trigger */
return 0;
}
#ifdef __STDC__
int period_to_tune(int period, char *tune)
#else
int period_to_tune(period, tune) int period; char *tune;
#endif
{ /* fill in tune = tune_name.finetune if period corresponds to a standard */
return 0;
}
void
#ifdef __STDC__
print_note(union u_note *p, int line)
#else
print_note(p, line) union u_note *p; int line;
#endif
{ /* print note in format [line] = sample:tune.finetune:effect(args) */
/* or sample:period:effect(args) */
char tune[20];
char *s;
int na, a1, a2;
printf("[%d]=",line);
if (SAMPLE_NUMBER(p)!=0) printf("%d",SAMPLE_NUMBER(p));
printf(":");
if (SAMPLE_PERIOD(p)!=0)
{
if (period_to_tune(SAMPLE_PERIOD(p), tune))
{
printf("%s", tune);
}
else
{
printf("%d",SAMPLE_PERIOD(p));
}
}
printf(":");
switch(EFFECT(p))
{
case Arpeggio:
if (ARG1(p)==0 && ARG2(p)==0) break;
goto standard;
case TonePortamento:
if (BIGARG(p)==0) goto noargs;
goto standard;
case Vibrato:
case Tremolo:
if (ARG1(p)==0 || ARG2(p)==0) goto noargs;
goto standard;
case TonePortamentoVolumeSlide:
case VibratoVolumeSlide:
case VolumeSlide:
if (ARG1(p)!=0 && ARG2(p)!=0) break;
goto standard;
case NOTUSED:
break;
case PositionJump:
if (BIGARG(p) >=num_patterns_in_song)
{
if (verbose) fprintf(stderr,"PositionJump out of range %d (max %d)\n",
BIGARG(p), num_patterns_in_song-1);
break;
}
goto standard;
case SetVolume:
goto limit64;
case PatternBreak:
goto bcd;
case SetSpeed:
if (BIGARG(p)==0) break;
goto standard;
case Extended:
{
switch(EXT_EFFECT(p))
{
case JumptoLoop:
if (SMALLARG(p)==0) goto setloop;
goto ext_standard;
case NOTUSED2:
break;
default:
ext_standard:
s = ext_effect_info[EXT_EFFECT(p)-SetFilter].name;
na = ext_effect_info[EXT_EFFECT(p)-SetFilter].number_of_args;
a1 = SMALLARG(p);
goto printit;
setloop:
s = "SetLoop";
na = 0;
goto printit;
}
}
default:
standard:
s = effect_info[EFFECT(p)].name;
na = effect_info[EFFECT(p)].number_of_args;
if (na==1) a1 = BIGARG(p);
else { a1 = ARG1(p); a2 = ARG2(p); }
goto printit;
limit64:
s = effect_info[EFFECT(p)].name;
na = effect_info[EFFECT(p)].number_of_args;
a1 = BIGARG(p);
if (a1 > 64) a1=64;
goto printit;
bcd:
s = effect_info[EFFECT(p)].name;
na = effect_info[EFFECT(p)].number_of_args;
a1 = ARG1(p) * 10 + ARG2(p);
goto printit;
noargs:
s = effect_info[EFFECT(p)].name;
na = 0;
printit:
printf("%s",s);
switch (na)
{
case 1: printf("(%d)", a1);
break;
case 2:
printf("(%d,%d)", a1,a2);
break;
}
}
}
#ifdef __STDC__
void dump_tracks( FILE *fi )
#else
void dump_tracks( fi ) FILE *fi ;
#endif
{
int j, tnum, num_notes, note_number_on_line, line, i;
union u_note *pattern;
if (fseek(fi,fixed_size,SEEK_SET)!=0)
err_exit("Badly formed MOD file, cant reach pattern data",NULL);
if ((pattern = (union u_note *)malloc(pattern_size))==NULL)
err_exit("Out of Memory for pattern buffer",NULL);
for(j=0; j < num_patterns; j++)
{
if (fread(pattern, pattern_size, 1, fi) != 1)
err_exit("Badly formed MOD file: cant read pattern %d",j);
if (!dump_unused_patterns)
{
/* test if it is a valid pattern */
for (i=0 ; i< num_patterns_in_song; i++)
{
if (j==pattern_table[i]) break;
}
if (i==num_patterns_in_song)
{
if (verbose) fprintf(stderr,"Skipping unused pattern %d\n",j);
continue; /* to next pattern */
}
}
if (verbose) printf("\n// Pattern %d, note format [line]=sample:tune:effect\n", j);
printf("assume pattern %d\n", j);
for (tnum = 0; tnum < channels; tnum++)
{
/* dump a track if there is info */
num_notes=0;
for (line=0; line < LINES_PER_PATTERN; line ++)
{
if (!note_empty(pattern+(line*channels)+tnum))
{
num_notes++;
}
}
if (num_notes)
{
if (verbose) printf(" // Pattern %d Track %d\n", j, tnum);
printf(" track %d = {", tnum);
note_number_on_line=0;
for (line=0; line < LINES_PER_PATTERN; line ++)
{
if (!note_empty(pattern+(line*channels)+tnum))
{
note_number_on_line++;
if (note_number_on_line==1 && num_notes > NOTES_PER_OUTPUT_LINE)
{
printf("\n\t");
}
else if (note_number_on_line!=1)
{
printf(",\t");
}
print_note(pattern+(line*channels)+tnum, line);
if (note_number_on_line == NOTES_PER_OUTPUT_LINE)
note_number_on_line = 0;
}
}
printf("%s}\n", num_notes > NOTES_PER_OUTPUT_LINE?"\n\t":" ");
}
} /* for tnum */
} /* for j */
free(pattern);
}
static FILE *fs;
static enum sample_format saved_sample_format;
static struct sample saved_samp;
#ifdef __STDC__
void dump_samples_initialize(enum sample_format s_format, struct sample samp,
uint32 len, int sample_number)
#else
void dump_samples_initialize(s_format, samp, len, sample_number)
enum sample_format s_format; struct sample samp;
uint32 len; int sample_number;
#endif
{
saved_sample_format = s_format;
saved_samp = samp;
switch(saved_sample_format)
{
case Internal:
if (verbose)
printf("\n// Data for Sample # %d, bytes: %lu\n", sample_number,
len);
printf("assume sample %d\n", sample_number);
printf(" sample_data = {\n"); /* } */
break;
case Ascii:
case Sample:
fs=fopen(samp.sample_filename, WRITEBINARY);
if (fs==NULL)
{
err_exit("Error: Can't open file %s for writing\n", samp.sample_filename);
}
break;
case Wave:
fs=wav_write(NULL, &samp, wav_sample_rate, wav_info_flag);
/* open file and write header */
break;
}
}
#ifdef __STDC__
void dump_samples_continue(signed char *sample_buff, uint32 nbytes)
#else
void dump_samples_continue(sample_buff, nbytes) signed char *sample_buff; uint32 nbytes;
#endif
{
uint32 j;
int i;
switch(saved_sample_format)
{
case Internal:
for(j=0; j<nbytes; j+=16)
{
for(i=0; i<16 && j+i < nbytes; i++)
{
printf( " %d,", sample_buff[j+i]);
}
printf("\n");
}
break;
case Ascii:
for(j=0; j<nbytes; j++)
{
if (fprintf(fs,"%d\n",sample_buff[j])==EOF)
{
err_exit("Error: unable to write to sample file", NULL);
}
}
break;
case Sample:
if (FWRITE(sample_buff, sizeof(signed char), nbytes, fs)!=nbytes)
{
err_exit("Error: unable to write to sample file", NULL);
}
break;
case Wave:
/* we write in unsigned format */
for(j=0; j<nbytes; j++)
{
unsigned char uc;
uc = s_to_u(sample_buff[j]);
if (putc(uc, fs)== EOF)
err_exit("Error: unable to write to sample file", NULL);
}
break;
}
}
#ifdef __STDC__
void dump_samples_finalize(void)
#else
void dump_samples_finalize()
#endif
{
switch(saved_sample_format)
{
case Internal:
/* { */
printf(" }\n");
break;
case Wave:
/* write any final info */
wav_write_final(fs, &saved_samp, wav_sample_rate, wav_info_flag);
/* drop thru */
case Ascii:
case Sample:
fclose(fs);
break;
}
}
#ifdef __STDC__
void dump_samples( FILE *fi, enum sample_format s_format)
#else
void dump_samples( fi, s_format) FILE *fi; enum sample_format s_format;
#endif
{
/* read and dump the samples */
signed char *sample_buff;
int j;
off_t off;
int32 len;
sample_buff = malloc(SAMPLE_BUFFER_SIZE);
if (sample_buff==NULL)
err_exit("Out of Memory for sample buffer",NULL);
/* calculate the file offsets of each sample */
for (j=0, off=PATTERN_SPACE+ fixed_size ; j<instruments; j++)
{
samples[j].data_offset = off + SAMPLE_IGNORE_BYTES;
len = (int32)(file_has_gaps? samples[j].orig_length : samples[j].length ) * 2;
off += len;
}
/* now seek to these places and read the samples */
for (j=0; j<instruments; j++)
{
if (samples[j].length != 0)
{
if (fseek(fi, samples[j].data_offset, SEEK_SET)!=0)
err_exit("Badly formed MOD file: cant seek to sample data at %lu", samples[j].data_offset);
len = (int32) samples[j].length * 2 - SAMPLE_IGNORE_BYTES;
dump_samples_initialize(s_format, samples[j], len, j);
while(len > 0)
{
uint32 nbytes = (len>SAMPLE_BUFFER_SIZE)?SAMPLE_BUFFER_SIZE:len;
if (FREAD(sample_buff, sizeof(signed char), nbytes, fi) != nbytes)
err_exit("Badly formed MOD file: cant read sample %d",j);
dump_samples_continue(sample_buff, nbytes);
len -= nbytes;
}
dump_samples_finalize();
}
}
free(sample_buff);
}
#ifdef __STDC__
FILE *open_mod_file(char *path)
#else
FILE *open_mod_file(path) char *path;
#endif
{
struct stat buf;
FILE *fi;
if (stat(path, &buf)!=0) err_exit("Cant stat file %s", path);
file_size = buf.st_size; /*set the file size later used in calculations*/
fi = fopen(path, READBINARY);
if (fi == NULL) err_exit("Cant open file %s", path);
return fi;
}
#ifdef __STDC__
void verify_port(void)
#else
void verify_port()
#endif
{ /* check that the port is OK */
int i;
signed char sc;
unsigned char uc;
union u_note un, *p;
assert(sizeof(uint32) == 4);
assert(sizeof(int32) == 4);
assert(sizeof(uint32) == 4);
assert(sizeof(int16) == 2);
assert(sizeof(uint16) == 2);
assert(sizeof(char) == 1);
assert(sizeof(unsigned char) == 1);
assert(sizeof(signed char) == 1);
assert(sizeof(union u_note) == NOTESIZE);
assert(sizeof(uint32) == NOTESIZE);
/* supports signed and unsigned char */
sc = -15;
i = -15;
assert(sc == i);
uc = sc;
assert(uc != i);
/* verify we have correct endian-ness */
p = &un;
#ifdef BE
#ifdef NO_CONSTANT_SUFFIX
un.note_word = 0x12345678;
#else
un.note_word = 0x12345678UL;
#endif
#else
#ifdef NO_CONSTANT_SUFFIX
un.note_word = 0x78563412;
#else
un.note_word = 0x78563412UL;
#endif
#endif
assert(SAMPLE_NUMBER(p)==0x15);
assert(SAMPLE_PERIOD(p)==0x234);
assert(EFFECT(p)==6);
assert(EXT_EFFECT(p)==0x67);
assert(BIGARG(p)==0x78);
assert(ARG1(p)==7);
assert(ARG2(p)==8);
assert(SMALLARG(p)==8);
/* verify fields are unsigned */
#ifdef BE
#ifdef NO_CONSTANT_SUFFIX
un.note_word = 0x9AB0CDEF;
#else
un.note_word = 0x9AB0CDEFUL;
#endif
#else
#ifdef NO_CONSTANT_SUFFIX
un.note_word = 0xEFCDB09A;
#else
un.note_word = 0xEFCDB09AUL;
#endif
#endif
assert(SAMPLE_NUMBER(p)==0x9C);
assert(SAMPLE_PERIOD(p)==0xAB0);
assert(EFFECT(p)==13);
assert(EXT_EFFECT(p)==0xDE);
assert(BIGARG(p)==0xEF);
assert(ARG1(p)==14);
assert(ARG2(p)==15);
assert(SMALLARG(p)==15);
/* check we can sign extend a nibble */
assert(SIGN_EXT_NIBBLE(EFFECT(p))==-3);
}
#ifdef __STDC__
void usage(char *program)
#else
void usage(program) char *program;
#endif
{
fprintf(stderr,"Usage: %s [options] file.mod >file.dis\n", program);
fprintf(stderr," options:\n");
fprintf(stderr," -c n = set number of channels (aka voices, tracks) to n\n");
fprintf(stderr," -i n = set number of instruments (aka samples) to n\n");
fprintf(stderr," without -c and -i, %s will attempt to figure out\n", program);
fprintf(stderr," the file format\n");
fprintf(stderr," -np = do not disassemble patterns\n");
fprintf(stderr," -ns = do not disassemble samples\n");
fprintf(stderr," -u = unused patterns\n");
fprintf(stderr," sometimes there are more patterns in the file than are actually used\n");
fprintf(stderr," in the song. By default these are ignored. With the -u flag they are\n");
fprintf(stderr," disassembled.\n");
fprintf(stderr," -v = verbose (another -v = more verbose)\n");
fprintf(stderr," -f x = format of samples, values of x are:\n");
fprintf(stderr," i=internal, s=raw samples (.sam),\n");
fprintf(stderr," a=ascii (.txt), w=Microsoft wave (.wav)\n");
fprintf(stderr," w1=.wav at 11025 Hz, no INFO\n");
fprintf(stderr," -p prefix = prefix of sample output files (defaults to inst)\n");
fprintf(stderr," -s suffix = suffix of sample output files (override format default)\n");
fprintf(stderr," file.mod = input file\n");
fprintf(stderr," file.dis = output file\n");
err_exit("Operation Aborted.",NULL);
}
int
#ifdef __STDC__
main(int argc, char **argv)
#else
main(argc, argv) int argc; char **argv;
#endif
{
FILE *fi=NULL;
int i;
char *fn=NULL;
enum sample_format s_format=Internal;
int no_pattern_flag=0, no_sample_flag=0;
char *prefix=NULL, *suffix=NULL;
verify_port(); /* a quick test the compiler is OK */
for(i=1; i< argc; i++)
{
if(argv[i][0]=='-')
{
switch(argv[i][1])
{
case 'n':
case 'N':
switch(argv[i][2])
{
case 'p':
case 'P':
no_pattern_flag =1;
continue;
case 's':
case 'S':
no_sample_flag = 1;
continue;
default:
usage(argv[0]);
}
case 'v':
verbose++;
continue;
case 'f':
++i;
switch(argv[i][0])
{
case 'i':
s_format = Internal;
continue;
case 's':
s_format = Sample;
suffix = ".sam";
continue;
case 'a':
s_format = Ascii;
suffix = ".txt";
continue;
case 'w':
s_format = Wave;
suffix = ".wav";
if (argv[i][1]=='1')
{
wav_sample_rate=11025;
wav_info_flag = 0;
}
continue;
default:
usage(argv[0]);
}
case 'p':
prefix = argv[++i];
continue;
case 's':
suffix = argv[++i];
continue;
case 'c':
sscanf(argv[++i], "%d", &channels);
continue;
case 'i':
sscanf(argv[++i], "%d", &instruments);
continue;
case 'u':
dump_unused_patterns=1;
continue;
default:
usage(argv[0]);
}
} /* - */
else if (fn != NULL)
{
usage(argv[0]);
}
else
{
fn = argv[i];
}
} /* for i */
if (fn==NULL) usage(argv[0]);
fi = open_mod_file(fn);
/* read file type */
if (channels == 0) read_tag(fi); /* try to figure out format */
if (verbose)
{
fprintf(stderr,"Tag: %s Instruments: %d Channels: %d\n",mod_tag,
instruments, channels);
}
/* verify file is OK */
verify_format(fi); /* also reads headers */
/* assign filenames to the samples */
if(s_format != Internal) assign_filenames(prefix, suffix);
/* write out the headers */
write_headers(fn);
write_sample_headers(s_format);
write_pattern_header();
/* read the patterns and write out the track info */
if (!no_pattern_flag) dump_tracks(fi);
/* read and dump the samples */
if (!no_sample_flag) dump_samples(fi, s_format);
exit(0);
return 0;
}
static char rcs_id[]="$Id: mod_dis.c,v 1.1 1994/03/19 09:21:31 dlai Exp $";
#if 0
$Log: mod_dis.c,v $
* Revision 1.1 1994/03/19 09:21:31 dlai
* Initial revision
*
#endif